package org.kernely.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.shiro.SecurityUtils;
import org.kernely.security.dao.UserDetailsDAO;
import org.kernely.security.persistence.User;
import org.kernely.security.persistence.UserDetails;
import org.kernely.ui.admin.KernelyAdminPresenter;
import org.kernely.ui.admin.KernelyAdminView;
import org.kernely.ui.home.KernelyHomePresenter;
import org.kernely.ui.home.KernelyHomeView;
import org.kernely.ui.mvp.IKernelyPresenter;
import org.kernely.ui.mvp.IKernelyView;
import org.kernely.ui.mvp.KernelyEvent;
import org.kernely.ui.mvp.KernelyEventReceiver;
import org.kernely.ui.mvp.KernelyPresenter;
import org.kernely.ui.mvp.KernelyViewPresenterManager;
import org.kernely.ui.profile.KernelyProfileEvent;
import org.kernely.ui.profile.KernelyProfilePresenter;
import org.kernely.ui.profile.KernelyProfileView;
import org.kernely.ui.profile.KernelyPublicProfilePresenter;
import org.kernely.ui.profile.KernelyPublicProfileView;
import org.kernely.ui.services.KernelyUIBundle;
import org.kernely.ui.settings.KernelySettingsPresenter;
import org.kernely.ui.settings.KernelySettingsView;
import org.kernely.ui.utils.DisplayProfileEvent;
import org.kernely.ui.utils.KernelyBundleMenu;

import com.vaadin.terminal.ExternalResource;
import com.vaadin.terminal.Sizeable;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Layout;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.MenuBar.MenuItem;

/**
 * The most high-leve presenter.
 * Manages all other presenters and display them when needed.
 */
public class MainApplicationPresenter extends KernelyPresenter<MainApplicationView> implements KernelyEventReceiver{

	/**
	 * The Serial Version ID
	 */
	private static final long serialVersionUID = 72541857764606790L;

	private IKernelyPresenter actualPresenter;
	private Map<String, KernelyViewPresenterManager> applicationViewPresenter = new HashMap<String, KernelyViewPresenterManager>();

	/**
	 * MainApplicationPresenter's contructor
	 */
	public MainApplicationPresenter(){
		// We create the presenters and insert it into the list.
		KernelyViewPresenterManager kernelyUIAssociation = new KernelyViewPresenterManager();

		kernelyUIAssociation.put(KernelyHomeView.class, KernelyHomePresenter.class);
		kernelyUIAssociation.put(KernelyProfileView.class, KernelyProfilePresenter.class);
		kernelyUIAssociation.put(KernelyAdminView.class, KernelyAdminPresenter.class);
		kernelyUIAssociation.put(KernelySettingsView.class, KernelySettingsPresenter.class);
		
		// Adding the static pages HOME, PROFILE and ADMIN
		applicationViewPresenter.put(MainApplication.getBundleName(), kernelyUIAssociation);
	}

	/**
	 * This method logout the user. He is automatically redirected to the KernelyLoginView (see the code in MainApplication).
	 */
	private void logout(){
		SecurityUtils.getSubject().logout();
	}

	/**
	 * @see org.kernely.core.service.IKernelyPresenter#bind()
	 */
	@Override
	public final void bind() {

		//Initialize the Home button in the menu bar
		MenuBar.Command goToHome = new MenuBar.Command() {
			private static final long serialVersionUID = 11604150612L;
			/**
			 * @see com.vaadin.ui.MenuBar.Command#menuSelected(com.vaadin.ui.MenuBar.MenuItem)
			 */
			public void menuSelected(MenuItem selectedItem) {
				showHomePage();
			}
		};
		this.view.getHomeMenu().addItem("Home", goToHome);

		//Initialize the Applications menu in the menu bar
		this.view.setApplications(this.view.getApplicationsMenu().addItem("Applications", null));
		initializeApplicationsMenu();


		//Initialize the Logout button in the menu bar
		MenuBar.Command logout = new MenuBar.Command(){
			/**
			 * the Serial Version ID
			 */
			private static final long serialVersionUID = -6084826317075580609L;

			/**
			 * @see com.vaadin.ui.MenuBar.Command#menuSelected(com.vaadin.ui.MenuBar.MenuItem)
			 */
			public void menuSelected(MenuItem selectedItem) {
				logout();
			}
		};
		this.view.getLogoutMenu().addItem("Logout", logout);

		//Initialize the user's menu
		User u = MainApplication.getSecurityService().getCurrentUser();
		UserDetailsDAO uddao = UserDetailsDAO.getInstance();
		UserDetails ud = uddao.getUserDetails(u);
		String currentUser = ud.getFirstname() + " " + ud.getName();

		this.view.setChangeProfileItem(this.view.getProfileMenu().addItem(currentUser, null));
		MenuBar.Command profile = new MenuBar.Command(){
			/**
			 * The Serial Version ID
			 */
			private static final long serialVersionUID = -8743922539554977153L;

			/**
			 * @see com.vaadin.ui.MenuBar.Command#menuSelected(com.vaadin.ui.MenuBar.MenuItem)
			 */
			public void menuSelected(MenuItem selectedItem) {
				showView(MainApplication.getBundleName(), KernelyProfileView.class);
			}
		};
		MenuItem profileItem = this.view.getUserMenuItem().addItem("Profile", profile);
		profileItem.setIcon(new ThemeResource("img/user_basic.png"));

		MenuBar.Command settings = new MenuBar.Command(){
			/**
			 * The Serial Version ID
			 */
			private static final long serialVersionUID = -1261011451098015153L;

			/**
			 * @see com.vaadin.ui.MenuBar.Command#menuSelected(com.vaadin.ui.MenuBar.MenuItem)
			 */
			public void menuSelected(MenuItem selectedItem) {
				showView(MainApplication.getBundleName(), KernelySettingsView.class);
			}
		};
		MenuItem settingsItem = this.view.getUserMenuItem().addItem("Settings", settings);
		settingsItem.setIcon(new ThemeResource("img/settings.png"));

		MainApplication.getEventBus().addListener(this);

		showHomePage();
		
	}

	private void showHomePage() {
		// Get the user preference for home page
		UserDetailsDAO uddao = UserDetailsDAO.getInstance();
		UserDetails details = uddao.getUserDetails(MainApplication.getSecurityService().getCurrentUser());
		String homePageBundle = details.getHomepagebundle();
		String homePageView = details.getHomepageview();
		Class<? extends IKernelyView> homePageViewClass = findViewClass(homePageBundle,homePageView);
		
		// Class not found or user have not set custom home page, try to get the default page set by the administrator
		if (homePageViewClass == null){
			homePageBundle = MainApplication.getSettingsService().getProperty(MainApplication.getBundleName(), MainApplication.getDbkeyHomepageBundle());
			homePageView = MainApplication.getSettingsService().getProperty(MainApplication.getBundleName(), MainApplication.getDbkeyHomepageClass());

			homePageViewClass = findViewClass(homePageBundle,homePageView);
		}

		// Default home page choose by the admin not found, get the Kernely home page
		if (homePageViewClass == null){
			homePageBundle = MainApplication.getBundleName();

			homePageViewClass = KernelyHomeView.class;
		}
		
		showView(homePageBundle,homePageViewClass);
	}
	
	private Class<? extends IKernelyView> findViewClass(String bundleName, String viewClass){
		KernelyViewPresenterManager manager = applicationViewPresenter.get(bundleName);
		if (manager != null){
			for (Class<? extends IKernelyView> view : manager.keySet()){
				if (view.toString().equals(viewClass)){
					return view;
				}
			}
		}
		return null;
	}

	/**
	 * This method initialize the applications menu, where bundles menu are displayed and where there is the Administration menu.
	 */
	@SuppressWarnings("serial")
	private void initializeApplicationsMenu(){
		this.view.getApplications().removeChildren();
		// Create a list of all KernelyBundle deployed
		ArrayList<KernelyUIBundle> bundlesList = new ArrayList<KernelyUIBundle>(MainApplication.getKernelyUIBundleService().getUIBundles());
		for (final KernelyUIBundle bundle : bundlesList){			
			KernelyBundleMenu bundlemenu = bundle.getMenu();
			if(bundlemenu != null){
				Set<Entry<String, Class<? extends IKernelyView>>> entryset = bundlemenu.entrySet();
				for(final Entry<String, Class<? extends IKernelyView>> e : entryset){
					String path = e.getKey();
					String[] splitted = path.split("/");
					MenuItem mi;
					Set<String> rolesSet = bundlemenu.getRoles(e.getValue());
					if (rolesSet == null || MainApplication.getSecurityService().currentUserHasOneOf(rolesSet)){
						if(!menuAlreadyExistsInMenuBar(splitted[0], view.getApplicationsMenu())){
							mi = view.getApplicationsMenu().addItem(splitted[0], null);
							String menuIcon = bundlemenu.getMenuIcon(splitted[0]);
							if (menuIcon != null){
								mi.setIcon(new ExternalResource(menuIcon));
							}
						}
						else{
							mi = getMenuItemWithCaption(splitted[0]);
						}
						if(splitted.length > 1){
							for(int i = 1; i < splitted.length; i++){
								if(i != splitted.length - 1){
									if(!menuAlreadyExistsInMenuItem(splitted[i], mi)){
										mi = mi.addItem(splitted[i], null);
										mi.setIcon(new ExternalResource(bundlemenu.getMenuIcon(splitted[i])));
									}
									else{
										mi = getMenuItemWithCaption(splitted[i]);
									}
								}
								else{
									
									MenuBar.Command mc = new MenuBar.Command() {
										/**
										 * @see com.vaadin.ui.MenuBar.Command#menuSelected(com.vaadin.ui.MenuBar.MenuItem)
										 */
											public void menuSelected(MenuItem selectedItem) {
											showView(bundle.getName(), e.getValue());
										}
									};
									if(!menuAlreadyExistsInMenuItem(splitted[i], mi)){
										mi = mi.addItem(splitted[i], mc);
										String icon = bundlemenu.getMenuIcon(splitted[i]);
										if (icon != null){
											mi.setIcon(new ExternalResource(icon));
										}
									}
								}
							}
						}
						else{
							MenuBar.Command mc = new MenuBar.Command() {
								/**
								 * @see com.vaadin.ui.MenuBar.Command#menuSelected(com.vaadin.ui.MenuBar.MenuItem)
								 */
								public void menuSelected(MenuItem selectedItem) {
									showView(bundle.getName(), e.getValue());
								}
							};
							mi.setCommand(mc);
						}
					}
				}
				applicationViewPresenter.put(bundle.getName(), bundle.getViewPresenter());
			}
		}

		// Only administrators can view the administration panel
		if(MainApplication.getSecurityService().currentUserHasOneOf("Administrator")){
			// The menu item to go to the administration panel.
			MenuBar.Command mc = new MenuBar.Command() {
				/**
				 * The Serial Version ID
				 */
				private static final long serialVersionUID = 1156131681L;

				/**
				 * @see com.vaadin.ui.MenuBar.Command#menuSelected(com.vaadin.ui.MenuBar.MenuItem)
				 */
				public void menuSelected(MenuItem selectedItem) {
					showView(MainApplication.getBundleName(), KernelyAdminView.class);
				}
			};
			MenuItem item = this.view.getApplicationsMenu().addItem("Administration",mc);
			item.setIcon(new ThemeResource("img/admin16.png"));
		}
	}
	
	/**
	 * Verify if an item exists at the same level in the menuBar
	 * @param caption : caption of the item
	 * @param menu : the menuBar
	 * @return true if exists
	 */
	private boolean menuAlreadyExistsInMenuBar(String caption, MenuBar menu){
		List<MenuBar.MenuItem> items = menu.getItems();
		for(MenuItem mi : items){
			if(mi.getText().equals(caption)){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Verify if an item exists at the same level in the MenuItem
	 * @param caption : caption of the item
	 * @param menu : the menuBar
	 * @return true if exists
	 */
	private boolean menuAlreadyExistsInMenuItem(String caption, MenuItem menu){
		List<MenuBar.MenuItem> items = menu.getChildren();
		if(items != null){
			for(MenuItem mi : items){
				if(mi.getText().equals(caption)){
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * Retrieve the item having this caption
	 * @param caption : the caption of the item
	 * @return the item corresponding
	 */
	private MenuItem getMenuItemWithCaption(String caption){
		List<MenuBar.MenuItem> items = view.getApplicationsMenu().getItems();
		for(MenuItem mi : items){
			if(mi.getText().equals(caption)){
				return mi;
			}
		}
		return null;
	}
	
	
	/**
	 * This method display a KernelyView into the center panel.
	 * @param bundleName The name of the bundle which contains the view
	 * @param viewClass The class to show (must extends IKernelyView).
	 */
	public final void showView(String bundleName, Class<? extends IKernelyView> viewClass){
		try {
			
			// Retrieve the presenter
			IKernelyPresenter presenter = ((KernelyViewPresenterManager) applicationViewPresenter.get(bundleName)).getPresenterInstance(viewClass);
			// Retrieve the view
			IKernelyView view = viewClass.newInstance();
			// Associate the view to the presenter
			presenter.setView(view);

			if (actualPresenter != null){
				actualPresenter.unset();				
			}
			actualPresenter = presenter;

			if(this.view.getCenterLayout() != null){
				this.view.removeComponent(this.view.getCenterLayout());
			}
			this.view.setCenterLayout((Layout)presenter.getView());
			Layout layout = (Layout)presenter.getView();
			layout.setWidth(1024, Sizeable.UNITS_PIXELS);
			this.view.addComponent(layout);
			this.view.setComponentAlignment(layout, Alignment.MIDDLE_CENTER);
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @see org.kernely.core.service.KernelyEventReceiver#handleViewEvent(KernelyEvent)
	 */
	@Override
	public final void handleKernelyEvent(KernelyEvent event) {
		if (event instanceof KernelyProfileEvent){
			// Update name in the menu bar
			//Initialize the user's menu
			User u = MainApplication.getSecurityService().getCurrentUser();
			UserDetailsDAO uddao = UserDetailsDAO.getInstance();
			UserDetails ud = uddao.getUserDetails(u);
			String currentUser = ud.getFirstname() + " " + ud.getName();
			this.view.getUserMenuItem().setText(currentUser);
		}
		if(event instanceof KernelyUIBundleDetectionEvent){
			initializeApplicationsMenu();
		}
		if (event instanceof DisplayProfileEvent){
			UserDetails details = ((DisplayProfileEvent) event).getDetails();

			// Build a presenter and its view
			IKernelyPresenter presenter = new KernelyPublicProfilePresenter(details);
			IKernelyView view = new KernelyPublicProfileView();
			// Associate the view to the presenter
			presenter.setView(view);

			if (actualPresenter != null){
				actualPresenter.unset();				
			}
			actualPresenter = presenter;

			if(this.view.getCenterLayout() != null){
				this.view.removeComponent(this.view.getCenterLayout());
			}

			this.view.setCenterLayout((Layout)presenter.getView());
			Layout layout = (Layout)presenter.getView();

			layout.setWidth(1024, Sizeable.UNITS_PIXELS);
			this.view.addComponent(layout);
			this.view.setComponentAlignment(layout, Alignment.MIDDLE_CENTER);
		}
	}
}
